package crp.extensions.scy.sso;

import crp.core.faculty.domain.models.FacultyEntity;
import crp.core.faculty.domain.repositories.FacultyRepository;
import crp.core.student.domain.models.StudentEntity;
import crp.core.student.domain.repositories.StudentRepository;
import crp.core.user.domain.models.UserEntity;
import crp.core.user.domain.repositories.UserRepository;
import crp.extensions.scy.sso.models.SCYUserInfo;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import java.util.Optional;

@ApplicationScoped
public class SCYSSOUserPersistence {
    private final FacultyRepository facultyRepository;
    private final StudentRepository studentRepository;
    private final UserRepository userRepository;

    @Inject
    public SCYSSOUserPersistence(FacultyRepository facultyRepository,
                                 StudentRepository studentRepository,
                                 UserRepository userRepository) {
        this.facultyRepository = facultyRepository;
        this.studentRepository = studentRepository;
        this.userRepository = userRepository;
    }

    @Transactional
    public Optional<UserEntity> persist(SCYUserInfo userInfo) {
        Optional<FacultyEntity> mayFaculty = userInfo.getFacultyNo()
                .map(facultyNo -> persistFaculty(facultyNo, userInfo.getRealName()));
        Optional<StudentEntity> mayStudent = userInfo.getStudentNo()
                .map(studentNo -> persistStudent(studentNo, userInfo.getRealName()));
        if (mayFaculty.isEmpty() && mayStudent.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(loadUser(mayFaculty, mayStudent));
    }

    private FacultyEntity persistFaculty(String facultyNo, String name) {
        return loadFaculty(facultyNo, name);
    }

    private StudentEntity persistStudent(String studentNo, String name) {
        return loadStudent(studentNo, name);
    }

    private StudentEntity loadStudent(String studentNo, String name) {
        return studentRepository.findByStudentNo(studentNo)
                .orElseGet(() -> {
                    StudentEntity student = new StudentEntity();
                    student.setStudentNo(studentNo);
                    student.setName(name);
                    studentRepository.persist(student);
                    return student;
                });
    }

    private UserEntity loadUser(Optional<FacultyEntity> mayFaculty,
                                Optional<StudentEntity> mayStudent) {
        Optional<UserEntity> mayFacultyUser = mayFaculty.flatMap(faculty -> userRepository.findByFacultyId(faculty.getId()));
        Optional<UserEntity> mayStudentUser = mayStudent.flatMap(student -> userRepository.findByStudentId(student.getId()));
        UserEntity user = mayFacultyUser.orElse(mayStudentUser.orElse(null));
        if (mayFacultyUser.isPresent() && mayStudentUser.isPresent()) {
            user.setFacultyId(mayFaculty.map(FacultyEntity::getId).orElse(null));
            user.setStudentId(mayStudent.map(StudentEntity::getId).orElse(null));
            userRepository.persist(user);
            return user;
        }
        if (mayFacultyUser.isEmpty() && mayStudentUser.isEmpty()) {
            return loadNewUser(mayFaculty, mayStudent);
        }
        return user;
    }

    private UserEntity loadNewUser(Optional<FacultyEntity> mayFaculty, Optional<StudentEntity> mayStudent) {
        String name = mayFaculty.map(FacultyEntity::getName)
                .orElse(mayStudent.map(StudentEntity::getName).orElse(null));
        UserEntity user = new UserEntity();
        user.setName(name);
        user.setFacultyId(mayFaculty.map(FacultyEntity::getId).orElse(null));
        user.setStudentId(mayStudent.map(StudentEntity::getId).orElse(null));
        userRepository.persist(user);
        return user;
    }

    private FacultyEntity loadFaculty(String facultyNo, String name) {
        return facultyRepository.findByFacultyNo(facultyNo)
                .orElseGet(() -> {
                    FacultyEntity faculty = new FacultyEntity();
                    faculty.setFacultyNo(facultyNo);
                    faculty.setName(name);
                    facultyRepository.persist(faculty);
                    return faculty;
                });
    }
}
